# -*- coding:utf-8 -*-

# 对运行完的用例列表进行统计.
# 划分领域.
# 统计结果:通过率, 响应时间.
# 往input目录输出所有用例的url, 以及能Pass用例对应的文本和url.

import os
import re
import time
import shutil
import cPickle

from ConfigParser import ConfigParser as Parser


class ResultHandler():

    def __init__(self, config_file):
        config = Parser()
        config.read(config_file)

        self.this_time_dir = (config.get('Result_Dir', 'this_time')).strip()
        self.pass_case_dir = (config.get('Result_Dir', 'pass_case')).strip()
        self.all_case_dir = (config.get('Result_Dir', 'all_case')).strip()

        self.version_info = (config.get('Result_Log', 'version_info')).strip()
        self.diff_log = (config.get('Result_Log', 'diff_log')).strip()
        self.commit_result = (config.get('Result_Log', 'commit_result')).strip()
        self.regress_log = (config.get('Result_Log', 'regression_log')).strip()
        self.failed_index = (config.get('Result_Log', 'failed_index')).strip()
        self.timeout_log = (config.get('Result_Log', 'timeout_log')).strip()
        self.dump_file = (config.get('Result_Log', 'dump_file')).strip()

    def init_logs(self):
        """
        1. 清空regression.log, 写入标题: Total    Pass    Fail等列名
        2. 清空timeout.log, 写入标题"超时的url如下:"
        3. 清空./results/this_time目录, 用于存放即将运行的结果.
        """
        with open(self.regress_log, 'w') as r_log:
            r_log.write("Total\tPass\tFail(timeout)\tPass_rate\tResps_time\tDomain_name\n")
            r_log.write('-' * 78 + '\n')
        
        # with open(self.timeout_log, 'w') as t_log:
        #     t_log.write("超时URL:\n")

        # with open(self.failed_index, 'w') as index:
        #     index.write("#### Failed Case Index ####\n")

        if os.path.exists(self.this_time_dir):
            for f in os.listdir(self.this_time_dir):
                os.remove(self.this_time_dir + f)
        else:
            os.mkdir(self.this_time_dir)

        for tar_dir in [self.pass_case_dir, self.all_case_dir]:
            if os.path.exists(tar_dir):
                shutil.rmtree(tar_dir)
            if not os.path.exists(tar_dir):
                os.makedirs(tar_dir)

    def calc_test_result(self, domain_list):
        """ 计算所有测试集/用例各项平均值并输出, 包括召回率"""
        t = p = r = f = o = tt = 0 # total, pass, recall, fail, timeout, totaltime.
        for domain in domain_list:
            self._write_domain_static(domain)
            t += domain.total
            p += domain.pass_
            r += domain.recall
            f += domain.fail
            o += domain.timeout
            tt += domain.total_time

        resp = tt * 1000 / t # ms
        acc  = float('%.2f' % (float(p) * 100 / float(t)))
        recall = float('%.2f' % (float(r) * 100 / float(t)))

        r = "Recall: %.2f%%\t(%d/%d)\n" % (recall, r, t)
        r += "   Acc: %.2f%%\tResp_Time: %.2fms\nTotal: %d\tPass: %d\tFail: %d\tTimeout: %d" % (acc, resp, t, p, f, o)
        
        with open(self.regress_log, 'a') as log:
            log.write('-' * 78 + '\n')
            log.write(r)
            log.write('\n' + '-' * 78 + '\n')

        print '-' * 50 + '\n' + r + '\n' + '-' * 50
        self._save_results(domain_list)

    def _save_results(self, domain_list):
        ''' fail用例详细结果, pass的URL和文本等.'''
        for domain in domain_list:
            self._output_passed_url(domain)
            self._output_failed_case(domain)
        # self._dump_all(domain_list)

    def _dump_all(self, domain_list):
        fp = file(self.dump_file, 'w')
        cPickle.dump(domain_list, fp)
        fp.close()
        print "dump domain_list to [%s], done" % self.dump_file

    def _write_domain_static(self, domain):
        """计算单个领域的用例准确率/召回率/响应时间等"""
        d = domain
        statics = (d.total, d.pass_, d.fail, d.timeout, d.pass_rate, d.resp_time, d.name)
        r = "%d\t%d\t%d(%d)\t%.2f\t%.2fms\t%s\n" % statics
        with open(self.regress_log, 'a') as log:
            log.write(r)

    def _output_passed_url(self, domain):
        """ output pass-case and all-case's url """
        all_url = file(os.path.join(self.all_case_dir, domain.name), 'w')
        pass_url = file(os.path.join(self.pass_case_dir, domain.name), 'w')


        for case in domain.case_list:
            all_url.write('%s\n' % case.url)
            if case.is_pass:
                pass_url.write('%s\t%s\n' % (case.txt, case.url))
        all_url.close()
        pass_url.close()

    def _output_failed_case(self, domain):
        """
        1. 输出domain的单项结果和用例失败的详细信息.
            "超时" 记录为TIMEOUT, 分别输出到timeout, failed, 和timeout_history日志中.
            "不匹配" 记录URL+返回结果+不匹配字段
        2. 输出"文件名\t失败用例文本" >> ./results/failed_index.log
        """
        
        #  index_log = open(self.failed_index, 'a')
        timeout_log = file(self.timeout_log, 'a')
        dname = domain.name
        if domain.fail > 0:
            fail_log = open(self.this_time_dir + str(domain.fail) + '_' + dname, 'w')
            
            for case in domain.case_list:
                txt = case.txt
                if domain.is_dialog and case.is_pass:
                    # 上下文对话输出成功用例的文本.
                    fail_log.write("%s\n%s\n" % (txt, case.url) + ">" * 30 + "\n")

                if not case.is_pass:
                    index_ln = '%s\t%s\n' % (dname, txt)
                    fail_ln = '用例: %s\n' % txt
                    fail_ln += '检查点: %s\n' % case.kv_checks
                    fail_ln += '不匹配字段:\n'
                    for unmatch in case.unmatch_list:
                        fail_ln += unmatch + '\n'
                    fail_ln += '-'*20 + '\n'
                    fail_ln += case.url + '\n'

                    fail_ln += '-'*20 + '\n'
                    if case.is_timeout:
                        fail_ln += 'Time Out[%s]!\n' % str(case.req_code)
                        index_ln += ' Time Out!\n'
                        timeout_log.write('%s\t%s\n' % (dname, txt))
                        timeout_log.write(case.url + '\n\n')
                    else:
                        fail_ln += case.resp_result + '\n\n'
                        fail_ln += '+'*50 + '\n\n'
                    fail_log.write(fail_ln)
                    #  index_log.write(index_ln)
            fail_log.close()

        #  index_log.close()
        timeout_log.close()

    def _get_last_release_version(self):
        return file(self.version_info).read().strip()

    def _get_last_release_result(self):
        rls_result = {}
        with open(self.commit_result) as commits:
            for ln in commits:
                rate = 0.0
                if '\t' in ln and '.txt' in ln:
                    try:
                        ln_parts = ln.strip().split('\t')
                        domain = ln_parts[-1]
                        rate = float(ln_parts[-3])
                        pass_out_of_total = "%s/%s" % (ln_parts[1], ln_parts[0])
                        rls_result[domain] = (rate, pass_out_of_total)
                    except:
                        print 'Ignored ValueError Line: ', ln
                elif '|' in ln:
                    ln_parts = ln.strip().split('\t')
                    domain = ln_parts[-1]
                    rate = float(ln.strip().split('|')[0])
                    pass_out_of_total = "%s/%s" % (ln_parts[1], ln_parts[0]) # ancient format, seems obsoleted
                    rls_result[domain] = (rate, pass_out_of_total)
        return rls_result

    def _get_sorted_diff_lns(self, diff_lns):
        # 按diff_lns中passed cases的下降数排序, pass数下降越多的领域越靠前(忽略变化数=0的领域).
        domain_diff_num_dict = {}
        rc = re.compile(r'\-?\d+\scases')
        
        for ln in diff_lns:
            diff_num = int(rc.findall(ln)[0].strip("cases"))
            if diff_num:
                domain_diff_num_dict[ln] = diff_num
        return sorted(domain_diff_num_dict.iteritems(), key=lambda d:d[1])

    def output_diff_log(self, domain_list, base_on_release=True):
        """ 本次结果 vs 上次release结果, 缺少/新增的领域用miss/newly标识
            <用例pass数变化>     <pass/total vs last_RC>  <domain_name>
            -10.00%(-7 cases)    90/100 vs 100/100        control_uhome_V2.txt
            12.00%(-15 cases)    125/125 vs 110/125       test_uhome_V2.txt
            newly(-3 cases)      35/43 vs 0               v5.2.5.txt
            miss(100 cases)      ...
        """
        last_version = self._get_last_release_version()
        last_result = self._get_last_release_result()

        output = file(self.diff_log, 'w')
        output.write('Diff this_time VS last_release_version %s:\n' % last_version)
        output.write('-' * 50 + '\n')

        diff_lns = []
        for domain in domain_list:
            for last_domain_name in last_result.keys():
                if last_domain_name == domain.name:
                    this_pas_of_tot = "%d/%d" % (domain.pass_, domain.total)
                    last_pas_of_tot = last_result[last_domain_name][1]
                    
                    this_rate = domain.pass_rate
                    last_rate = last_result[last_domain_name][0]

                    diff_rate = this_rate - last_rate
                    diff_num = domain.pass_ - int(last_pas_of_tot.split('/')[0])
                    single_ln = "%.2f" % diff_rate + "%"
                    single_ln += "(%d cases)\t%s vs %s\t%s\n" % (diff_num, this_pas_of_tot, last_pas_of_tot, last_domain_name)
                    # print single_ln
                    diff_lns.append(single_ln)
        
        # sort and output diff_domain_results
        for sorted_ln in self._get_sorted_diff_lns(diff_lns):
            output.write(sorted_ln[0])

        new_domain_name_list = [d.name for d in domain_list]
        last_domain_name_list = last_result.keys()
        # append newly domain results
        for d in domain_list:
            if d.name not in last_domain_name_list:
                newly_ln = 'newly\t%.2f VS 0.00\t %s\n' % (d.pass_rate, d.name)
                output.write(newly_ln)

        # append miss domain results
        if base_on_release:
            for name in last_domain_name_list:
                if name not in new_domain_name_list:
                    miss_ln = 'miss\t0.00 VS %.2f\t %s\n' % (last_result[name][0], name)
                    output.write(miss_ln)

        output.close()
        self._show_diff()

    def _show_diff(self):
        print '\n\n' + '-' * 100
        with open(self.diff_log) as diff:
            for ln in diff:
                if not ln.startswith('0.00') and not ln.startswith('-0.00') :
                    print ln.strip()
